#pragma once

#include <vex/iterable/concept/one_pass_range_concept.h>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <utility>
#include <cassert>

namespace vex { namespace iterable { 
    
    namespace stl_interop {
        /// This iterator can be used for writing c++11 range based loops.
        template<class TRange>
        class iterator_from_range : public boost::iterator_facade<
            iterator_from_range<TRange>,
            typename range_traits<TRange>::value_type,
            typename std::input_iterator_tag,
            typename range_traits<TRange>::reference,
            decltype(
                std::declval<typename range_traits<TRange>::pointer>() - 
                std::declval<typename range_traits<TRange>::pointer>())
        >
        {
            BOOST_CONCEPT_ASSERT((concept::OnePassRange<TRange>));
            typedef typename std::decay<TRange>::type range_type;
            range_type* m_range;
            friend class boost::iterator_core_access;

            typename iterable::range_traits<TRange>::reference dereference() const
            {
                assert(m_range);
                return m_range->front();
            }

            bool equal(iterator_from_range const & p_other) const
            {
                return m_range == p_other.m_range;
            }

            void increment()
            {
                assert(m_range);
                move_front_and_reset_if_ended();
            }

            void move_front_and_reset_if_ended()
            {
                if (!m_range->move_front()) {
                    m_range = nullptr;
                }
            }
        public:
            iterator_from_range(range_type & p_range)
                : m_range(std::addressof(p_range)) 
            {
                // assuming iterable is not started...
                move_front_and_reset_if_ended();
            }

            iterator_from_range(iterator_from_range && p_other)
                : m_range(nullptr) 
            {
                std::swap(m_range, p_other.m_range);
            }

            iterator_from_range()
                : m_range(nullptr) {}
        };
    }

    /// <summary>
    /// Creates a stl iterator range which satisfies the OnePassRange concept from given range. 
    /// Attention: the passed <paramref name="TRange"/> instance is consumed by using the iterators!
    /// </summary>
    template<class TRange> auto make_stl_iterator_range(TRange & p_range)
        -> boost::iterator_range< stl_interop::iterator_from_range<TRange> >
    {
        typedef stl_interop::iterator_from_range<TRange> iterator_type;
        return boost::make_iterator_range(iterator_type(p_range), iterator_type());
    }
}}